package rexsee.core.application;

import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import rexsee.core.browser.Browser;
import rexsee.core.browser.clazz.ActivityResult.ActivityResultListener;
import rexsee.core.browser.clazz.JavascriptInterface;
import rexsee.core.utilities.Json;
import rexsee.core.utilities.RexseeClazz;
import rexsee.core.utilities.Utilities;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;

public class RexseeIntent implements JavascriptInterface {

	public static final String INTERFACE_NAME = "Intent";
	@Override
	public String getInterfaceName() {
		return mBrowser.application.resources.prefix + INTERFACE_NAME;
	}
	@Override
	public JavascriptInterface getInheritInterface(Browser childBrowser) {
		return this;
	}
	@Override
	public JavascriptInterface getNewInterface(Browser childBrowser) {
		return new RexseeIntent(childBrowser);
	}

	private static final String ARRAY_SPLITER = "\\|";

	private static final String TYPE_BOOLEAN = "boolean";
	private static final String TYPE_BOOLEAN_ARRAY = "booleanarray";
	private static final String TYPE_BYTE = "byte";
	private static final String TYPE_BYTE_ARRAY = "bytearray";
	private static final String TYPE_CHAR = "char";
	private static final String TYPE_CHAR_ARRAY = "chararray";
	private static final String TYPE_INT = "int";
	private static final String TYPE_INT_ARRAY = "intarray";
	private static final String TYPE_SHORT = "short";
	private static final String TYPE_SHORT_ARRAY = "shortarray";
	private static final String TYPE_LONG = "long";
	private static final String TYPE_LONG_ARRAY = "longarray";
	private static final String TYPE_FLOAT = "float";
	private static final String TYPE_FLOAT_ARRAY = "floatarray";
	private static final String TYPE_DOUBLE = "double";
	private static final String TYPE_DOUBLE_ARRAY = "doublearray";
	private static final String TYPE_STRING = "string";
	private static final String TYPE_STRING_ARRAY = "stringarray";
	private static final String TYPE_CHARSEQUENCE = "charsequence";
	private static final String TYPE_CHARSEQUENCE_ARRAY = "charsequencearray";
	private static final String TYPE_URI = "uri";

	private static final String EVENT_INTENT_CALLBACK = "onIntentCallback";
	private static final String EVENT_INTENT_BROADCAST_CALLBACK = "onIntentBroadcastCallback";
	private static final String EVENT_INTENT_BROADCASTLISTENER_CALLBACK = "onIntentBroadcastReceived";

	private final Context mContext;
	private final Browser mBrowser;
	private Bundle mBundle = null;
	private Intent mIntent = null;

	private final HashMap<String, BroadcastReceiver> mMap;

	public RexseeIntent(Browser browser) {
		mBrowser = browser;
		mContext = browser.getContext();
		browser.eventList.add(EVENT_INTENT_CALLBACK);
		browser.eventList.add(EVENT_INTENT_BROADCAST_CALLBACK);
		browser.eventList.add(EVENT_INTENT_BROADCASTLISTENER_CALLBACK);
		mMap = new HashMap<String, BroadcastReceiver>();
	}

	public String getAllDefaultActions() {
		java.lang.reflect.Field[] fields = Intent.class.getFields();
		String rtn = "";
		for (int i = 0; i < fields.length; i++) {
			String name = fields[i].getName();
			if (name.startsWith("ACTION_")) {
				if (!rtn.equals("")) rtn += ",";
				try {
					rtn += "\"" + String.valueOf(fields[i].get(null)) + "\"";
				} catch (Exception e) {
				}
			}
		}
		return "[" + rtn + "]";
	}
	public String getAllDefaultCategories() {
		java.lang.reflect.Field[] fields = Intent.class.getFields();
		String rtn = "";
		for (int i = 0; i < fields.length; i++) {
			String name = fields[i].getName();
			if (name.startsWith("CATEGORY_")) {
				if (!rtn.equals("")) rtn += ",";
				try {
					rtn += "\"" + String.valueOf(fields[i].get(null)) + "\"";
				} catch (Exception e) {
				}
			}
		}
		return "[" + rtn + "]";
	}
	public String getAllDefaultExtras() {
		java.lang.reflect.Field[] fields = Intent.class.getFields();
		String rtn = "";
		for (int i = 0; i < fields.length; i++) {
			String name = fields[i].getName();
			if (name.startsWith("EXTRA_")) {
				if (!rtn.equals("")) rtn += ",";
				try {
					rtn += "\"" + String.valueOf(fields[i].get(null)) + "\"";
				} catch (Exception e) {
				}
			}
		}
		return "[" + rtn + "]";
	}
	public String getAllDefaultFlags() {
		java.lang.reflect.Field[] fields = Intent.class.getFields();
		String rtn = "";
		for (int i = 0; i < fields.length; i++) {
			String name = fields[i].getName();
			if (name.startsWith("FLAG_")) {
				if (!rtn.equals("")) rtn += ",";
				rtn += "\"" + name + "\"";
			}
		}
		return "[" + rtn + "]";
	}
	public int getFlag(String flag) {
		java.lang.reflect.Field[] fields = Intent.class.getFields();
		for (int i = 0; i < fields.length; i++) {
			if (flag.equals(fields[i].getName())) {
				try {
					return fields[i].getInt(null);
				} catch (Exception e) {
				}
			}
		}
		return -1;
	}

	public boolean isReady() {
		return (mIntent == null) ? false : true;
	}
	public void create() {
		if (mIntent == null) mIntent = new Intent();
	}
	public void clear() {
		mIntent = null;
	}
	public void reCreate() {
		clear();
		create();
	}

	//Action, category, data, type and flag
	public void setAction(String action) {
		create();
		mIntent.setAction(action);
	}
	public String getAction() {
		return (mIntent == null) ? "" : mIntent.getAction();
	}
	public void setComponent(String packageName, String className) {
		create();
		if (packageName == null || packageName.equals("")) return;
		if (className == null || className.equals("")) {
			mIntent.setPackage(packageName);
		} else {
			mIntent.setClassName(packageName, className);
		}
	}
	public String getPackageName() {
		if (mIntent == null) return "";
		ComponentName component = mIntent.getComponent();
		if (mIntent.getComponent() == null) return "";
		return component.getPackageName();
	}
	public String getClassName() {
		if (mIntent == null) return "";
		ComponentName component = mIntent.getComponent();
		if (mIntent.getComponent() == null) return "";
		return component.getClassName();
	}
	public String getShortClassName() {
		if (mIntent == null) return "";
		ComponentName component = mIntent.getComponent();
		if (mIntent.getComponent() == null) return "";
		return component.getShortClassName();
	}
	public void addCategory(String category) {
		create();
		mIntent.addCategory(category);
	}
	public String getCategories() {
		if (mIntent == null) return "[]";
		Set<String> categories = mIntent.getCategories();
		if (categories == null) return "[]";
		Iterator<String> iterator = categories.iterator();
		String rtn = "";
		while (iterator.hasNext()) {
			if (!rtn.equals("")) rtn += ",";
			rtn += "\"" + iterator.next() + "\"";
		}
		return "[" + rtn + "]";
	}
	public void setData(String data) {
		create();
		mIntent.setData(Uri.parse(data));
	}
	public void setDataAndType(String data) {
		setDataAndType(data, Utilities.getMime(data));
	}
	public void setDataAndType(String data, String type) {
		create();
		mIntent.setDataAndType(Uri.parse(data), type);
	}
	public String getData() {
		if (mIntent == null) return "";
		return mIntent.getDataString();
	}
	public void setType(String type) {
		create();
		mIntent.setType(type);
	}
	public String getType() {
		if (mIntent == null) return "";
		return mIntent.getType();
	}
	public void addFlag(String flag) {
		int iFlag = getFlag(flag);
		if (iFlag < 0) {
			mBrowser.exception(getInterfaceName(), "Invalid flag: " + flag);
		} else {
			create();
			mIntent.addFlags(iFlag);
		}
	}
	public int getFlags() {
		if (mIntent == null) return -1;
		return mIntent.getFlags();
	}

	public String get() {
		if (mIntent == null) return "";
		if (Integer.parseInt(Build.VERSION.SDK) >= 4) {
			return mIntent.toUri(Intent.URI_INTENT_SCHEME).toString();
		} else {
			return mIntent.toURI().toString();
		}
	}
	public boolean set(String uri) {
		if (Integer.parseInt(Build.VERSION.SDK) >= 4) {
			mIntent = null;
			try {
				mIntent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
				return true;
			} catch (URISyntaxException e) {
				mBrowser.exception(getInterfaceName(), e);
				return false;
			}
		} else {
			mBrowser.exception(getInterfaceName(), "Too lower SDK version.");
			return false;
		}
	}

	//Extra
	public void removeExtra(String key) {
		if (mBundle != null) {
			mBundle.remove(key);
		}
	}
	public void removeExtras() {
		mBundle = null;
	}
	public boolean hasExtra(String key) {
		if (mBundle == null) return false;
		return mBundle.containsKey(key);
	}
	public boolean hasExtra() {
		if (mBundle == null) return false;
		return !mBundle.isEmpty();
	}
	public int getNumberOfExtras() {
		if (mBundle == null) return 0;
		return mBundle.size();
	}
	public void putExtra(String key, String type, String value) {
		if (mBundle == null) mBundle = new Bundle();
		type = type.toLowerCase();
		if (type.equals(TYPE_BOOLEAN)) {
			mBundle.putBoolean(key, Boolean.parseBoolean(value));
		} else if (type.equals(TYPE_BOOLEAN_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			boolean[] values = new boolean[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Boolean.parseBoolean(array[i]);
			}
			mBundle.putBooleanArray(key, values);
		} else if (type.equals(TYPE_BYTE)) {
			mBundle.putByte(key, (byte) Integer.parseInt(value));
		} else if (type.equals(TYPE_BYTE_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			byte[] values = new byte[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = (byte) Integer.parseInt(array[i]);
			}
			mBundle.putByteArray(key, values);
		} else if (type.equals(TYPE_CHAR)) {
			mBundle.putChar(key, value.charAt(0));
		} else if (type.equals(TYPE_CHAR_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			char[] values = new char[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = array[i].charAt(0);
			}
			mBundle.putCharArray(key, values);
		} else if (type.equals(TYPE_INT)) {
			mBundle.putInt(key, Integer.parseInt(value));
		} else if (type.equals(TYPE_INT_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			int[] values = new int[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Integer.parseInt(array[i]);
			}
			mBundle.putIntArray(key, values);
		} else if (type.equals(TYPE_SHORT)) {
			mBundle.putShort(key, Short.parseShort(value));
		} else if (type.equals(TYPE_SHORT_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			short[] values = new short[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Short.parseShort(array[i]);
			}
			mBundle.putShortArray(key, values);
		} else if (type.equals(TYPE_LONG)) {
			mBundle.putLong(key, Long.parseLong(value));
		} else if (type.equals(TYPE_LONG_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			long[] values = new long[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Long.parseLong(array[i]);
			}
			mBundle.putLongArray(key, values);
		} else if (type.equals(TYPE_FLOAT)) {
			mBundle.putFloat(key, Float.parseFloat(value));
		} else if (type.equals(TYPE_FLOAT_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			float[] values = new float[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Float.parseFloat(array[i]);
			}
			mBundle.putFloatArray(key, values);
		} else if (type.equals(TYPE_DOUBLE)) {
			mBundle.putDouble(key, Double.parseDouble(value));
		} else if (type.equals(TYPE_DOUBLE_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			double[] values = new double[array.length];
			for (int i = 0; i < values.length; i++) {
				values[i] = Double.parseDouble(array[i]);
			}
			mBundle.putDoubleArray(key, values);
		} else if (type.equals(TYPE_STRING)) {
			mBundle.putString(key, value);
		} else if (type.equals(TYPE_STRING_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			mBundle.putStringArray(key, array);
		} else if (type.equals(TYPE_CHARSEQUENCE)) {
			mBundle.putCharSequence(key, Html.fromHtml(value));
		} else if (type.equals(TYPE_CHARSEQUENCE_ARRAY)) {
			String[] array = value.split(ARRAY_SPLITER);
			CharSequence[] values = new CharSequence[array.length];
			for (int i = 0; i < values.length; i++)
				values[i] = Html.fromHtml(array[i]);
			try {
				Method method = RexseeClazz.getMethod(Bundle.class, "putCharSequenceArray");
				if (method != null) method.invoke(mBundle, new Object[]{key, values});
				else mBrowser.exception(getInterfaceName(), "Too lower SDK version, need 2.2+.");
			} catch (Exception e) {
				mBrowser.exception(getInterfaceName(), "Too lower SDK version, need 2.2+.");
			}
		} else if (type.equals(TYPE_URI)) {
			mBundle.putParcelable(key, Uri.parse(value));
		} else {
			mBrowser.exception(getInterfaceName(), "Invalid extra type.");
		}
	}
	public String getExtra(String key, String type) {
		if (mBundle == null) return "";
		if (type.equalsIgnoreCase(TYPE_BOOLEAN)) {
			return String.valueOf(mBundle.getBoolean(key));
		} else if (type.equalsIgnoreCase(TYPE_BOOLEAN_ARRAY)) {
			return Json.toJson(mBundle.getBooleanArray(key));
		} else if (type.equalsIgnoreCase(TYPE_BYTE)) {
			return String.valueOf(mBundle.getByte(key));
		} else if (type.equalsIgnoreCase(TYPE_BYTE_ARRAY)) {
			return Json.toJson(mBundle.getByteArray(key));
		} else if (type.equalsIgnoreCase(TYPE_CHAR)) {
			return String.valueOf(mBundle.getChar(key));
		} else if (type.equalsIgnoreCase(TYPE_CHAR_ARRAY)) {
			return Json.toJson(mBundle.getCharArray(key));
		} else if (type.equalsIgnoreCase(TYPE_INT)) {
			return String.valueOf(mBundle.getInt(key));
		} else if (type.equalsIgnoreCase(TYPE_INT_ARRAY)) {
			return Json.toJson(mBundle.getIntArray(key));
		} else if (type.equalsIgnoreCase(TYPE_SHORT)) {
			return String.valueOf(mBundle.getShort(key));
		} else if (type.equalsIgnoreCase(TYPE_SHORT_ARRAY)) {
			return Json.toJson(mBundle.getShortArray(key));
		} else if (type.equalsIgnoreCase(TYPE_LONG)) {
			return String.valueOf(mBundle.getLong(key));
		} else if (type.equalsIgnoreCase(TYPE_LONG_ARRAY)) {
			return Json.toJson(mBundle.getLongArray(key));
		} else if (type.equalsIgnoreCase(TYPE_FLOAT)) {
			return String.valueOf(mBundle.getFloat(key));
		} else if (type.equalsIgnoreCase(TYPE_FLOAT_ARRAY)) {
			return Json.toJson(mBundle.getFloatArray(key));
		} else if (type.equalsIgnoreCase(TYPE_DOUBLE)) {
			return String.valueOf(mBundle.getDouble(key));
		} else if (type.equalsIgnoreCase(TYPE_DOUBLE_ARRAY)) {
			return Json.toJson(mBundle.getDoubleArray(key));
		} else if (type.equalsIgnoreCase(TYPE_STRING)) {
			return mBundle.getString(key);
		} else if (type.equalsIgnoreCase(TYPE_STRING_ARRAY)) {
			return Json.toJson(mBundle.getStringArray(key));
		} else if (type.equalsIgnoreCase(TYPE_CHARSEQUENCE)) {
			return String.valueOf(mBundle.getCharSequence(key));
		} else if (type.equalsIgnoreCase(TYPE_CHARSEQUENCE_ARRAY)) {
			try {
				Method method = RexseeClazz.getMethod(Bundle.class, "getCharSequenceArray");
				if (method != null) {
					return Json.toJson((CharSequence[]) method.invoke(mBundle, new Object[]{key}));
				} else {
					mBrowser.exception(getInterfaceName(), "Too lower SDK version, need 2.2+.");
					return "[]";
				}
			} catch (Exception e) {
				mBrowser.exception(getInterfaceName(), "Too lower SDK version, need 2.2+.");
				return "[]";
			}
		} else {
			mBrowser.exception(getInterfaceName(), "Invalid extra type: " + type);
			return "[]";
		}
	}
	public String getExtras() {
		if (mBundle == null) return "";
		Set<String> keys = mBundle.keySet();
		if (keys == null) return "";
		Iterator<String> iterator = keys.iterator();
		if (iterator == null) return "";
		String rtn = "";
		while (iterator.hasNext()) {
			if (!rtn.equals("")) rtn += ",";
			rtn += "\"" + iterator.next() + "\"";
		}
		return "[" + rtn + "]";
	}

	//Start activity
	public void chooseStart(String chooseTitle) {
		try {
			if (mIntent == null) return;
			if (mBundle != null) mIntent.putExtras(mBundle);
			Intent chooser = Intent.createChooser(mIntent, chooseTitle);
			mContext.startActivity(chooser);
		} catch (Exception e) {
			mBrowser.exception(getInterfaceName(), e);
		}
	}
	public void chooseStartWithCallback(String chooseTitle) {
		try {
			if (mIntent == null) return;
			if (mBundle != null) mIntent.putExtras(mBundle);
			Intent chooser = Intent.createChooser(mIntent, chooseTitle);
			mBrowser.activityResult.start(chooser, new ActivityResultListener() {
				@Override
				public void run(int resultCode, Intent resultIntent) {
					String oriAction = mIntent.getAction();
					String oriData = mIntent.getDataString();
					mIntent = resultIntent;
					mBrowser.eventList.run(EVENT_INTENT_CALLBACK, new String[]{oriAction, oriData, String.valueOf(resultCode)});
				}
			});
		} catch (Exception e) {
			mBrowser.exception(getInterfaceName(), e);
		}
	}
	public void start() {
		try {
			if (mIntent == null) return;
			if (mBundle != null) mIntent.putExtras(mBundle);
			mContext.startActivity(mIntent);
		} catch (Exception e) {
			mBrowser.exception(getInterfaceName(), e);
		}
	}
	public void startWithCallback() {
		try {
			if (mIntent == null) return;
			if (mBundle != null) mIntent.putExtras(mBundle);
			mBrowser.activityResult.start(mIntent, new ActivityResultListener() {
				@Override
				public void run(int resultCode, Intent resultIntent) {
					String oriAction = mIntent.getAction();
					String oriData = mIntent.getDataString();
					mIntent = resultIntent;
					mBrowser.eventList.run(EVENT_INTENT_CALLBACK, new String[]{oriAction, oriData, String.valueOf(resultCode)});
				}
			});
		} catch (Exception e) {
			mBrowser.exception(getInterfaceName(), e);
		}
	}

	//Listen to broadcast
	public boolean registerBroadcastReceiver(final String action) {
		if (mMap.containsKey(action)) return false;
		else {
			BroadcastReceiver receiver = new BroadcastReceiver() {
				@Override
				public void onReceive(Context context, Intent intent) {
					String uri;
					if (Integer.parseInt(Build.VERSION.SDK) >= 4) {
						uri = intent.toUri(Intent.URI_INTENT_SCHEME).toString();
					} else {
						uri = intent.toURI().toString();
					}
					mIntent = intent;
					mBrowser.eventList.run(EVENT_INTENT_BROADCASTLISTENER_CALLBACK, new String[]{action, uri});
				}
			};
			mMap.put(action, receiver);
			mContext.registerReceiver(receiver, new IntentFilter(action));
			return true;
		}
	}
	public void unregisterBroadcastReceiver(String action) {
		if (mMap.containsKey(action)) {
			BroadcastReceiver receiver = mMap.get(action);
			mContext.unregisterReceiver(receiver);
			mMap.remove(action);
		}
	}
	public void unregisterBroadcastReceiver() {
		Object[] keys = mMap.keySet().toArray();
		for (int i = 0; i < keys.length; i++) {
			BroadcastReceiver receiver = mMap.get(keys[i].toString());
			mContext.unregisterReceiver(receiver);
		}
		mMap.clear();
	}
	public boolean isRegisteredBroadcastReceiver(String action) {
		return mMap.containsKey(action);
	}
	public String getBroadcastReceivers() {
		Object[] keys = mMap.keySet().toArray();
		String rtn = "";
		for (int i = 0; i < keys.length; i++) {
			rtn += (i == 0) ? "\"" + keys[i].toString() + "\"" : ",\"" + keys[i].toString() + "\"";
		}
		return "[" + rtn + "]";
	}
	public int getNumberOfBroadcastReceivers() {
		return mMap.size();
	}

	//Broadcast
	public void broadcast() {
		broadcast(null);
	}
	public void broadcast(String permission) {
		if (mIntent == null) return;
		if (mBundle != null) mIntent.putExtras(mBundle);
		if (permission == null || permission.equals("")) {
			mContext.sendBroadcast(mIntent);
		} else {
			mContext.sendBroadcast(mIntent, permission);
		}
	}
	public void broadcastWithCallback(String permission) {
		if (mIntent == null) return;
		if (mBundle != null) mIntent.putExtras(mBundle);
		BroadcastReceiver resultReceiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				String oriAction = mIntent.getAction();
				String oriData = mIntent.getDataString();
				mIntent = intent;
				mBrowser.eventList.run(EVENT_INTENT_BROADCAST_CALLBACK, new String[]{oriAction, oriData});
			}
		};
		if (permission == null || permission.equals("")) {
			mContext.sendOrderedBroadcast(mIntent, null, resultReceiver, null, Activity.RESULT_OK, null, null);
		} else {
			mContext.sendOrderedBroadcast(mIntent, permission, resultReceiver, null, Activity.RESULT_OK, null, null);
		}
	}

	//Return to others
	public boolean copyInitIntent() {
		if (mBrowser.initIntent != null) {
			mIntent = mBrowser.initIntent;
			return true;
		} else {
			return false;
		}
	}
	public void quit(boolean resultOK) {
		if (mIntent == null) {
			((Activity) mContext).setResult(((resultOK) ? Activity.RESULT_OK : Activity.RESULT_CANCELED));
		} else {
			if (mBundle != null) mIntent.putExtras(mBundle);
			((Activity) mContext).setResult(((resultOK) ? Activity.RESULT_OK : Activity.RESULT_CANCELED), mIntent);
		}
		((Activity) mContext).finish();
	}

}
